package org.huatek.anders.utils.jwt;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.huatek.anders.constants.Constants;
import org.joda.time.DateTime;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.UUID;

/**
 * @author anders
 * @date 2018-12-03 14:59
 **/
@Slf4j
public class JwtToken {
    private static final SignatureAlgorithm JWT_ALG = SignatureAlgorithm.HS256;

    private static SecretKey generalKey() {
        try {
            byte[] secret = Constants.JWT_TOKEN_SECRET.getBytes(StandardCharsets.UTF_8);
            return new SecretKeySpec(secret, 0, secret.length, "AES");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 构建JWT
     * @param sub      jwt 面向的用户
     * @param jti      jwt 唯一身份标识
     * @param time jwt 有效时间，单位：秒
     * @return JWT字符串
     */
    private static String buildJWT(String sub, String jti,  long time) {
        time = time < 1 ? Constants.JWT_TOKEN_TIME : time * 1000;
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(jti)
                .setIssuer(Constants.JWT_ISS)
                .setSubject(sub)
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey);
        long expMillis = nowMillis + time;
        Date expDate = new Date(expMillis);
        builder.setExpiration(expDate);
        return builder.compact();
    }
    public static String generalSubject(JwtSubject subjectModel) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.writeValueAsString(subjectModel);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String buildJWT(String sub, long time) {
        return buildJWT(sub, UUID.randomUUID().toString(), time);
    }
    /**
     * 解析JWT
     * @param claimsJws jwt 内容文本
     * @return {@link Jws}
     */
    private static Jws<Claims> parseJWT(String claimsJws) {
        // 解析 JWT 字符串
        return Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(claimsJws);
    }

    /**
     * 校验JWT
     * @param token jwt 内容文本
     * @return JwtCheck 1 正确 -1认证失败 -2token过期
     */
    public static JwtCheck checkToken(String token) {
        JwtCheck jwtCheck = new JwtCheck(-1,null);
        if (token!=null){
            try {
                String subject = getSubject(token);
                jwtCheck.setSubject(subject);
                jwtCheck.setCode(subject == null ? -2 : 1);
            } catch (SignatureException | MalformedJwtException e) {
                jwtCheck.setCode(-1);
            } catch (ExpiredJwtException e) {
                jwtCheck.setCode(-2);
            }
        }
        return jwtCheck;
    }


    public static String getToken(HttpServletRequest request, String tokenName) {
        String token;
        if ((token = request.getHeader(tokenName)) != null) {
            return token;
        } else {
            return (token = (String)request.getAttribute(tokenName)) == null && (token = request.getParameter(tokenName)) != null ? token : token;
        }
    }
    public static String getSubject(String token){
        try {
            // 获取 JWT 的 payload 部分
            Claims claims = parseJWT(token).getBody();
            if (claims == null || claims.getExpiration().before(new Date())) {
                return null;
            }
            // 获取Subject
            return claims.getSubject();
        } catch (Exception e) {
            log.warn("JWT获取Subject出错，错误原因：{}", e.getMessage());
            return null;
        }
    }
}
